let path = require('path')
let fs = require('fs')
let Koa = require('koa')
let Router = require('koa-router')
let views = require('koa-views')
let utils = require('../utils')

/**
 * 这样写起来好奇怪
 * 到处都是副作用 side effect
 * tag v2
 */

let {
  readFile,
  readFiles,
  promisify,
  openBrowser
} = utils

let app = new Koa()
let router = new Router()
let lstat = promisify(fs.lstat)
let exists = promisify(fs.exists)
let readlink = promisify(fs.readlink)
let cwd = process.cwd()
let port = 9000

// 加载模板引擎
app.use(views(path.join(cwd, './src/views'), {
  extension: 'ejs'
}))

router.use(async (ctx, next) => {
  let locals = {}
  let pathname = ctx.path
  let filename = path.join(cwd, pathname)
  let isExist = await exists(filename)
  if (!isExist) {
    return await handleNotFound(ctx)
  }
  let file = await lstat(filename)
  if (!file) {
    return await handleNotFound(ctx)
  }
  /**
   * ISSUE
   * 为什么一定要 await next()
   * next() not working
   * 去除代码也不行
   */
  if (pathname) {
    locals.pathname = pathname
  }
  if (filename) {
    locals.filename = filename
  }
  locals.isFile = file && file.isFile()
  locals.isSymbolicLink = file && file.isSymbolicLink()
  locals.isDirectory = file && file.isDirectory()
  ctx.locals = locals
  if (locals.isSymbolicLink) {
    let link = await readlink(filename)    
    filename = path.join(cwd, path.dirname(pathname), link)
  }
  await next()
})

router.get('/', handleRenderFiles)
router.get('*', handler)

app.use(router.routes()).listen({ port }, () => {
  openBrowser(`http://localhost:${port}`)
})

async function handler(ctx) {
  let { isFile, isSymbolicLink, isDirectory } = ctx.locals
  if (isFile) {
    await handleRenderFile(ctx)
  } else if (isSymbolicLink || isDirectory) {
    await handleRenderFiles(ctx)
  }
}

async function handleRenderFiles(ctx) {
  let { filename, pathname } = ctx.locals
  let list = await getDirectoryFiles(filename, { pathname })
  await ctx.render('index', { list })
}

async function handleRenderFile(ctx) {
  let { filename } = ctx.locals
  ctx.type = path.extname(filename) || 'text/plain'
  ctx.body = await readFile(filename)
}

async function handleNotFound(ctx) {
  await ctx.render('404')
}

async function getDirectoryFiles(directory, { pathname }) {
  let files = await readFiles(directory)
  let list = files.map(file => {
    return {
      ...file,
      href: path.join(pathname, file.name)
    }
  })
  if (pathname !== '/') {
    list = [...getSuperiorCatalogue(pathname)].concat(list)
  }
  return list
}

function getSuperiorCatalogue(pathname) {
  return [
    {
      name: '..',
      href: path.dirname(pathname)
    }
  ]
}
